package com.cn.nio;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;

import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Set;

public class NioServer2 {

  static Logger logger = LoggerFactory.getLogger(NioServer2.class);

  public static void main(String[] args) {

    try {

      Selector selector = Selector.open();

      //  A selectable channel for stream-oriented listening sockets.
      ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
      serverSocketChannel.bind(new InetSocketAddress(9090));
      serverSocketChannel.configureBlocking(false);
      serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

      /**
       * 两个问题
       *  1 configureBlocking(false); 是啥？ 不设置还报错
       *  2 读取事件注册后，为啥必须 关闭 或者 注册 其他事件
       */
      while (true) {

        //阻塞
        selector.select();
        Set<SelectionKey> selectionKeys = selector.selectedKeys();
        Iterator<SelectionKey> iterator = selectionKeys.iterator();
        while (iterator.hasNext()) {
          SelectionKey selectionKey = iterator.next();
          //注册过就移除
          iterator.remove();

          if (selectionKey.isAcceptable()) { //连接
            logger.info("accept:{}", selectionKey);
            ServerSocketChannel newserverSocketChannel = (ServerSocketChannel) selectionKey.channel();
            // 必须设置 false 不然报错。
            newserverSocketChannel.configureBlocking(false);
            // 这个地方要accept 一个 socketChannel
            SocketChannel socketChannel = newserverSocketChannel.accept();
            // 必须设置 false 不然报错。 必须设置在 socketChannel 上面
            socketChannel.configureBlocking(false);
            socketChannel.register(selector, SelectionKey.OP_READ);

          } else if (selectionKey.isReadable()) {  // 读取
            SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
            logger.info("isReadable done -> {}", socketChannel);
            ByteBuffer allocate = ByteBuffer.allocate(1024);
            socketChannel.read(allocate);
            socketChannel.register(selector, SelectionKey.OP_WRITE);
            logger.info("read:{}", new String(allocate.array()));
          } else if (selectionKey.isWritable()) { // 输出
            SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
            logger.info("isWritable done -> {}", socketChannel);
            ByteBuffer allocate = ByteBuffer.allocate(1024);
            allocate.put("i`m server".getBytes(StandardCharsets.UTF_8));
            allocate.flip();
            socketChannel.write(allocate);
            socketChannel.close();
            logger.info("write done");
          }

        }
      }

    } catch (IOException e) {
      e.printStackTrace();
    }

  }

}

